/**
 * Sencha GXT 3.0.0b - Sencha for GWT
 * Copyright(c) 2007-2012, Sencha, Inc.
 * licensing@sencha.com
 *
 * http://www.sencha.com/products/gxt/license/
 */
package com.sencha.gxt.explorer.client.chart;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.editor.client.Editor.Path;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.cell.core.client.NumberCell;
import com.sencha.gxt.chart.client.chart.Chart;
import com.sencha.gxt.chart.client.chart.Chart.Position;
import com.sencha.gxt.chart.client.chart.axis.CategoryAxis;
import com.sencha.gxt.chart.client.chart.axis.RadialAxis;
import com.sencha.gxt.chart.client.chart.series.BarSeries;
import com.sencha.gxt.chart.client.chart.series.Primitives;
import com.sencha.gxt.chart.client.chart.series.RadarSeries;
import com.sencha.gxt.chart.client.chart.series.SeriesHighlighter;
import com.sencha.gxt.chart.client.chart.series.SeriesLabelConfig;
import com.sencha.gxt.chart.client.chart.series.SeriesRenderer;
import com.sencha.gxt.chart.client.draw.Color;
import com.sencha.gxt.chart.client.draw.RGB;
import com.sencha.gxt.chart.client.draw.sprite.RectangleSprite;
import com.sencha.gxt.chart.client.draw.sprite.Sprite;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite.TextAnchor;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite.TextBaseline;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.PropertyAccess;
import com.sencha.gxt.examples.resources.client.model.Data;
import com.sencha.gxt.explorer.client.model.Example.Detail;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.FramedPanel;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData;
import com.sencha.gxt.widget.core.client.container.SimpleContainer;
import com.sencha.gxt.widget.core.client.container.VBoxLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VBoxLayoutContainer.VBoxLayoutAlign;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.SortChangeEvent;
import com.sencha.gxt.widget.core.client.event.SortChangeEvent.SortChangeHandler;
import com.sencha.gxt.widget.core.client.form.FieldLabel;
import com.sencha.gxt.widget.core.client.form.NumberPropertyEditor.DoublePropertyEditor;
import com.sencha.gxt.widget.core.client.form.SpinnerField;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;

@Detail(name = "Dashboard", icon = "dashboard", category = "Charts", classes = Data.class)
public class DashboardExample implements IsWidget, EntryPoint {

  public interface DataPropertyAccess extends PropertyAccess<Data> {
    ValueProvider<Data, Double> data1();

    ValueProvider<Data, Double> data2();

    ValueProvider<Data, Double> data3();

    ValueProvider<Data, Double> data4();

    ValueProvider<Data, Double> data5();

    ValueProvider<Data, String> name();

    @Path("name")
    ModelKeyProvider<Data> nameKey();
  }

  private static final DataPropertyAccess dataAccess = GWT.create(DataPropertyAccess.class);
  private final ListStore<Data> store = new ListStore<Data>(dataAccess.nameKey());
  private final ListStore<Data> radarStore = new ListStore<Data>(dataAccess.nameKey());
  private final TextField name = new TextField();
  private final SpinnerField<Double> price = new SpinnerField<Double>(new DoublePropertyEditor());
  private final SpinnerField<Double> revenue = new SpinnerField<Double>(new DoublePropertyEditor());
  private final SpinnerField<Double> growth = new SpinnerField<Double>(new DoublePropertyEditor());
  private final SpinnerField<Double> product = new SpinnerField<Double>(new DoublePropertyEditor());
  private final SpinnerField<Double> market = new SpinnerField<Double>(new DoublePropertyEditor());
  private Data currentData;
  private Chart<Data> radarChart;

  private final static String[] radarLabels = {"Price", "Revenue %", "Growth %", "Product %", "Market %"};

  private final static String[] companies = {
      "3m Co", "Alcoa Inc", "Altria Group Inc", "American Express Company", "American International Group, Inc.",
      "AT&T Inc", "Boeing Co.", "Caterpillar Inc.", "Citigroup, Inc.", "E.I. du Pont de Nemours and Company",
      "Exxon Mobil Corp", "General Electric Company", "General Motors Corporation", "Hewlett-Packard Co",
      "Honeywell Intl Inc", "Intel Corporation", "International Business Machines", "Johnson & Johnson",
      "JP Morgan & Chase & Co", "McDonald\"s Corporation", "Merck & Co., Inc.", "Microsoft Corporation", "Pfizer Inc",
      "The Coca-Cola Company", "The Home Depot, Inc.", "The Procter & Gamble Company",
      "United Technologies Corporation", "Verizon Communications", "Wal-Mart Stores, Inc."};

  @Override
  public Widget asWidget() {
    for (int i = 0; i < companies.length; i++) {
      store.add(new Data(companies[i], Math.random() * 100, Math.random() * 100, Math.random() * 100,
          Math.random() * 100, Math.random() * 100, 0, 0, 0, 0));
    }

    final Chart<Data> barChart = new Chart<Data>();
    barChart.setStore(store);
    barChart.setShadowChart(true);
    barChart.setAnimated(true);

    CategoryAxis<Data, String> catAxis = new CategoryAxis<Data, String>();
    catAxis.setPosition(Position.BOTTOM);
    catAxis.setField(dataAccess.name());
    TextSprite rotation = new TextSprite();
    rotation.setRotation(270);
    catAxis.setLabelConfig(rotation);
    catAxis.setLabelProvider(new LabelProvider<String>() {
      @Override
      public String getLabel(String item) {
        if (item.length() > 8) {
          return item.substring(0, 8) + "...";
        } else {
          return item;
        }
      }
    });
    barChart.addAxis(catAxis);

    final BarSeries<Data> bar = new BarSeries<Data>();
    bar.setYAxisPosition(Position.LEFT);
    bar.addYField(dataAccess.data1());
    SeriesLabelConfig<Data> barLabelConfig = new SeriesLabelConfig<Data>();
    rotation = rotation.copy();
    rotation.setTextAnchor(TextAnchor.END);
    rotation.setTextBaseline(TextBaseline.MIDDLE);
    barLabelConfig.setSpriteConfig(rotation);
    bar.setLabelConfig(barLabelConfig);
    bar.addColor(new RGB(148, 174, 10));
    bar.setColumn(true);
    bar.setHighlighter(new SeriesHighlighter() {
      @Override
      public void highlight(Sprite sprite) {
        if (sprite instanceof RectangleSprite) {
          RectangleSprite bar = (RectangleSprite) sprite;
          bar.setStroke(new RGB(85, 85, 204));
          bar.setStrokeWidth(3);
          bar.setFill(new RGB("#a2b5ca"));
          bar.redraw();
        }
      }

      @Override
      public void unHighlight(Sprite sprite) {
        if (sprite instanceof RectangleSprite) {
          RectangleSprite bar = (RectangleSprite) sprite;
          bar.setStroke(Color.NONE);
          bar.setStrokeWidth(0);
          bar.setFill(new RGB(148, 174, 10));
          bar.redraw();
        }
      }
    });
    barChart.addSeries(bar);

    ContentPanel eastPanel = new ContentPanel();
    eastPanel.setHeadingText("Company Details");
    eastPanel.addStyleName("white-bg");
    VBoxLayoutContainer vbox = new VBoxLayoutContainer();

    name.setReadOnly(true);
    FieldLabel nameLabel = new FieldLabel(name, "Name");

    price.setIncrement(1d);
    price.setMinValue(0d);
    price.setMaxValue(100d);
    price.setAllowBlank(false);
    price.getPropertyEditor().setFormat(NumberFormat.getFormat("0.00"));
    price.addValueChangeHandler(new ValueChangeHandler<Double>() {
      @Override
      public void onValueChange(ValueChangeEvent<Double> event) {
        if (currentData != null) {
          Double value = event.getValue();
          if (value != null) {
            int storeIndex = store.indexOf(currentData);
            currentData.setData1(value);
            store.update(currentData);
            bar.drawSeries();
            bar.highlight(storeIndex);
            updateRadarStore(storeIndex);
            radarChart.redrawChart();
          }
        }
      }
    });
    FieldLabel priceLabel = new FieldLabel(price, "Price $");

    revenue.setIncrement(1d);
    revenue.setMinValue(0d);
    revenue.setMaxValue(100d);
    revenue.setAllowBlank(false);
    revenue.getPropertyEditor().setFormat(NumberFormat.getFormat("0.00"));
    revenue.addValueChangeHandler(new ValueChangeHandler<Double>() {
      @Override
      public void onValueChange(ValueChangeEvent<Double> event) {
        if (currentData != null) {
          Double value = event.getValue();
          if (value != null) {
            int storeIndex = store.indexOf(currentData);
            currentData.setData2(value);
            store.update(currentData);
            updateRadarStore(storeIndex);
            radarChart.redrawChart();
          }
        }
      }
    });
    FieldLabel revenueLabel = new FieldLabel(revenue, "Revenue %");

    growth.setIncrement(1d);
    growth.setMinValue(0d);
    growth.setMaxValue(100d);
    growth.setAllowBlank(false);
    growth.getPropertyEditor().setFormat(NumberFormat.getFormat("0.00"));
    growth.addValueChangeHandler(new ValueChangeHandler<Double>() {
      @Override
      public void onValueChange(ValueChangeEvent<Double> event) {
        if (currentData != null) {
          Double value = event.getValue();
          if (value != null) {
            int storeIndex = store.indexOf(currentData);
            currentData.setData3(value);
            store.update(currentData);
            updateRadarStore(storeIndex);
            radarChart.redrawChart();
          }
        }
      }
    });
    FieldLabel growthLabel = new FieldLabel(growth, "Growth %");

    product.setIncrement(1d);
    product.setMinValue(0d);
    product.setMaxValue(100d);
    product.setAllowBlank(false);
    product.getPropertyEditor().setFormat(NumberFormat.getFormat("0.00"));
    product.addValueChangeHandler(new ValueChangeHandler<Double>() {
      @Override
      public void onValueChange(ValueChangeEvent<Double> event) {
        if (currentData != null) {
          Double value = event.getValue();
          if (value != null) {
            int storeIndex = store.indexOf(currentData);
            currentData.setData4(value);
            store.update(currentData);
            updateRadarStore(storeIndex);
            radarChart.redrawChart();
          }
        }
      }
    });
    FieldLabel productLabel = new FieldLabel(product, "Product %");

    market.setIncrement(1d);
    market.setMinValue(0d);
    market.setMaxValue(100d);
    market.setAllowBlank(false);
    market.getPropertyEditor().setFormat(NumberFormat.getFormat("0.00"));
    market.addValueChangeHandler(new ValueChangeHandler<Double>() {
      @Override
      public void onValueChange(ValueChangeEvent<Double> event) {
        if (currentData != null) {
          Double value = event.getValue();
          if (value != null) {
            int storeIndex = store.indexOf(currentData);
            currentData.setData5(event.getValue());
            store.update(currentData);
            updateRadarStore(storeIndex);
            radarChart.redrawChart();
          }
        }
      }
    });
    FieldLabel marketLabel = new FieldLabel(market, "Market %");

    radarChart = createRadar();
    vbox.setVBoxLayoutAlign(VBoxLayoutAlign.CENTER);
    vbox.add(nameLabel);
    vbox.add(priceLabel);
    vbox.add(revenueLabel);
    vbox.add(growthLabel);
    vbox.add(productLabel);
    vbox.add(marketLabel);
    vbox.add(radarChart);
    eastPanel.add(vbox, new VerticalLayoutData(1, 1, new Margins(20, 0, 0, 0)));

    ContentPanel centerPanel = new ContentPanel();
    centerPanel.setHeadingText("Company Data");

    List<ColumnConfig<Data, ?>> columns = new ArrayList<ColumnConfig<Data, ?>>();
    columns.add(new ColumnConfig<Data, String>(dataAccess.name(), 120, "Name"));
    ColumnConfig<Data, Double> priceColumnConfig = new ColumnConfig<Data, Double>(dataAccess.data1(), 75, "Price $");
    priceColumnConfig.setCell(new NumberCell<Double>(NumberFormat.getFormat("0.00")));
    columns.add(priceColumnConfig);
    ColumnConfig<Data, Double> revenueColumnConfig = new ColumnConfig<Data, Double>(dataAccess.data2(), 75, "Revenue %");
    revenueColumnConfig.setCell(new NumberCell<Double>(NumberFormat.getFormat("0.00")));
    columns.add(revenueColumnConfig);
    ColumnConfig<Data, Double> growthColumnConfig = new ColumnConfig<Data, Double>(dataAccess.data3(), 75, "Growth %");
    growthColumnConfig.setCell(new NumberCell<Double>(NumberFormat.getFormat("0.00")));
    columns.add(growthColumnConfig);
    ColumnConfig<Data, Double> productColumnConfig = new ColumnConfig<Data, Double>(dataAccess.data4(), 75, "Product %");
    productColumnConfig.setCell(new NumberCell<Double>(NumberFormat.getFormat("0.00")));
    columns.add(productColumnConfig);
    ColumnConfig<Data, Double> marketColumnConfig = new ColumnConfig<Data, Double>(dataAccess.data5(), 75, "Market %");
    marketColumnConfig.setCell(new NumberCell<Double>(NumberFormat.getFormat("0.00")));
    columns.add(marketColumnConfig);

    Grid<Data> grid = new Grid<Data>(store, new ColumnModel<Data>(columns));
    grid.getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler<Data>() {
      @Override
      public void onSelectionChanged(SelectionChangedEvent<Data> event) {
        if (event.getSelection().size() > 0) {
          int index = store.indexOf(event.getSelection().get(0));
          if (currentData != null) {
            bar.unHighlight(store.indexOf(currentData));
            currentData = null;
          }
          if (index >= 0) {
            bar.highlight(index);
            currentData = event.getSelection().get(0);
            price.setValue(currentData.getData1());
            // update radar chart
            updateRadarStore(index);
            radarChart.redrawChart();
          }
        }
      }
    });
    grid.addSortChangeHandler(new SortChangeHandler() {
      @Override
      public void onSortChange(SortChangeEvent event) {
        barChart.redrawChart();
      }
    });
    centerPanel.add(grid);

    BorderLayoutContainer container = new BorderLayoutContainer();
    BorderLayoutData centerLayoutData = new BorderLayoutData();
    centerLayoutData.setMargins(new Margins(5, 5, 0, 0));
    container.setCenterWidget(centerPanel, centerLayoutData);
    SimpleContainer barChartContainer = new SimpleContainer();
    barChartContainer.add(barChart);
    barChartContainer.setBorders(true);
    container.setNorthWidget(barChartContainer, new BorderLayoutData(200));
    BorderLayoutData eastLayoutData = new BorderLayoutData(330);
    eastLayoutData.setMargins(new Margins(5, 0, 0, 0));
    container.setEastWidget(eastPanel, eastLayoutData);

    FramedPanel panel = new FramedPanel();
    panel.getElement().getStyle().setMargin(10, Unit.PX);
    panel.setHeadingText("Company Data");
    panel.setPixelSize(870, 720);
    panel.add(container);
    return panel;
  }

  public void onModuleLoad() {
    RootPanel.get().add(asWidget());
  }

  private Chart<Data> createRadar() {
    currentData = store.get(0);
    updateRadarStore(0);

    final Chart<Data> chart = new Chart<Data>(320, 320);
    chart.setStore(radarStore);
    chart.setAnimated(true);
    chart.setDefaultInsets(50);

    RadialAxis<Data, String> axis = new RadialAxis<Data, String>();
    axis.setCategoryField(dataAccess.name());
    axis.setSteps(5);
    chart.addAxis(axis);

    RadarSeries<Data> radar = new RadarSeries<Data>();
    radar.setYField(dataAccess.data1());
    radar.setFill(new RGB(194, 214, 240));
    radar.setStrokeWidth(0.5);
    radar.setLineRenderer(new SeriesRenderer<Data>() {
      @Override
      public void spriteRenderer(Sprite sprite, int index, ListStore<Data> store) {
        sprite.setOpacity(0.5);
      }
    });
    radar.setShowMarkers(true);
    Sprite marker = Primitives.circle(0, 0, 4);
    marker.setFill(new RGB(69, 109, 159));
    radar.setMarkerConfig(marker);
    chart.addSeries(radar);

    return chart;
  }

  private void updateRadarStore(int index) {
    radarStore.clear();
    Data data = store.get(index);
    name.setValue(data.getName());
    radarStore.add(new Data(radarLabels[0], data.getData1(), 0, 0, 0, 0, 0, 0, 0, 0));
    price.setValue(data.getData1());
    radarStore.add(new Data(radarLabels[1], data.getData2(), 0, 0, 0, 0, 0, 0, 0, 0));
    revenue.setValue(data.getData2());
    radarStore.add(new Data(radarLabels[2], data.getData3(), 0, 0, 0, 0, 0, 0, 0, 0));
    growth.setValue(data.getData3());
    radarStore.add(new Data(radarLabels[3], data.getData4(), 0, 0, 0, 0, 0, 0, 0, 0));
    product.setValue(data.getData4());
    radarStore.add(new Data(radarLabels[4], data.getData5(), 0, 0, 0, 0, 0, 0, 0, 0));
    market.setValue(data.getData5());
  }
}
